package com.edev.support.ddd.join;

import com.edev.support.dao.BasicDao;
import com.edev.support.ddd.DddException;
import com.edev.support.dsl.Join;
import com.edev.support.entity.Entity;
import lombok.NonNull;

import java.io.Serializable;
import java.util.*;

/**
 * 多对一关系处理器
 * <p>
 * 核心功能：
 * 1. 处理基于外键的多对一关系映射
 * 2. 在仓储层实现关联实体的加载
 * 3. 通过Join配置自动装配关联对象
 */
public class ManyToOneForJoin<E extends Entity<S>, S extends Serializable> extends AbstractRelation<E,S> {
    /**
     * 构造方法
     * @param join 关系配置对象（包含joinKey、name等元数据）
     * @param dao 数据访问对象（用于从数据库中读取并加载关联实体）
     */
    public ManyToOneForJoin(@NonNull Join join, @NonNull BasicDao dao) {
        super(join, dao);
    }

    /**
     * 插入值操作
     * <p>
     * 方法说明：
     * 由于多对一关系不可能出现聚合关系，仅进行设计校验，不执行任何操作。
     */
    @Override
    public void insertValue(E entity) {
        haveNoAggregation(entity);
    }

    /**
     * 更新值操作
     * <p>
     * 方法说明：
     * 由于多对一关系不可能出现聚合关系，仅进行设计校验，不执行任何操作。
     */
    @Override
    public void updateValue(E entity) {
        haveNoAggregation(entity);
    }

    /**
     * 删除值操作
     * <p>
     * 方法说明：
     * 由于多对一关系不可能出现聚合关系，仅进行设计校验，不执行任何操作。
     */
    @Override
    public void deleteValue(E entity) {
        haveNoAggregation(entity);
    }

    /**
     * 对单个实体加载关联实体
     * <p>
     * 处理流程：
     * 1. 从实体获取外键值
     * 2. 通过DAO从数据库或缓存中获取关联实体
     * 3. 将关联实体加载到原实体中
     * @param entity 需要加载关联实体的实体对象
     */
    @Override
    public void setValue(E entity) {
        if(entity==null) return;
        String joinKey = join.getJoinKey();
        @SuppressWarnings("unchecked")
        S id = (S) entity.getValue(joinKey);
        if(id==null) return;
        Entity<S> value = dao.load(id, getClazz());
        if(value==null) return;
        entity.setValue(join.getName(), value);
    }

    /**
     * 批量对多个实体加载关联实体（优化版）
     * <p>
     * 性能优化策略：
     * 1. 批量收集所有实体需要关联的外键ID
     * 2. 单次批量从数据库或缓存中查找关联实体
     * 3. 建立ID-实体映射表，快速完成关联实体对实体对象的装配
     * @param list 需要加载关联实体的实体集合
     */
    @Override
    public void setValueForList(Collection<E> list) {
        if(list==null||list.isEmpty()) return;
        String joinKey = join.getJoinKey();
        List<S> ids = new ArrayList<>();
        list.forEach(entity->{
            @SuppressWarnings("unchecked")
            S value = (S)entity.getValue(joinKey);
            if(value!=null) ids.add(value);
        });
        if(ids.isEmpty()) return;
        Collection<Entity<S>> valueList = dao.loadForList(ids, getClazz());
        if(valueList==null||valueList.isEmpty()) return;
        setListOfValuesToEachEntity(list, valueList);
    }

    /**
     * 为实体集合中的每个实体加载关联实体
     * <p>
     * 方法说明：
     * 1. 遍历整个关联实体集合，建立ID-实体映射表
     * 2. 通过ID-实体映射表，快速完成关联实体对实体对象的装配
     * @param list 实体集合
     * @param valueList 关联实体集合
     */
    private void setListOfValuesToEachEntity(Collection<E> list, Collection<Entity<S>> valueList) {
        if(list==null||valueList==null) return;
        Map<S,Entity<S>> mapOfEntitiesNeedJoin = new HashMap<>();
        valueList.forEach(value->mapOfEntitiesNeedJoin.put(value.getId(), value));
        list.forEach(entity->{
            String joinKey = join.getJoinKey();
            Entity<S> value = mapOfEntitiesNeedJoin.get(entity.getValue(joinKey));
            if(value==null) return;
            entity.setValue(join.getName(), value);
        });
    }

    /**
     * 校验聚合设计约束，多对一关系不允许出现聚合关系，如果出现则抛出异常。
     * @param entity 待校验的实体对象
     * @throws DddException 当配置为聚合关系时抛出
     */
    private void haveNoAggregation(E entity) {
        if(entity==null||!join.isAggregation()) return;
        throw new DddException("no aggregation for many-to-one relation! Check your design: %s", entity);
    }
}
